package steampunk.gui;

import java.awt.*;
import javax.swing.*;

import steampunk.GameVars;
import steampunk.PlayerControls;
import steampunk.art.Art;

import java.awt.event.*;
import java.awt.image.*;
import java.util.Hashtable;
import java.util.Random;

/**
 * This class draws the game's main content to the screen
 * @author garrett
 *
 */
public class Canvas extends JButton {

	private GameVars gv;
	
	private Hashtable<Integer, Color> colorCache;
	
	private PlayerControls controls;
	
	/** The back buffer used to prevent jittering */
	private BufferedImage buffer;
	
	public Canvas() {
		setPreferredSize(new Dimension(
				(int)(640*1.5), (int)(480*1.5)));
		this.gv = GameVars.getInstance();
		gv.wireColor = Color.BLACK;
		controls = gv.getPlayerControls();
		
		colorCache = new Hashtable<Integer, Color>();
		
		Repainter repainter = new Repainter();
		repainter.start();
		
		Listener l = new Listener();
		addKeyListener(l);
		addMouseListener(l);
	}
	
	/**
	 * Used to make it so that we don't have to constantly
	 * create new colors
	 * @param color
	 * @return
	 */
	private synchronized Color getColor(int color) {
		if (colorCache.containsKey(color)) {
			return colorCache.get(color);
		}
		Color c = new Color(color);
		colorCache.put(color, c);
		return c;
	}
	
	
	
	@Override
	/**
	 * This is what actually draws the game on the screen.
	 */
	public void paint(Graphics gg) {
		
		gg.setColor(Color.WHITE);
		gg.fillRect(-1, -1, getWidth()+2, getHeight()+2);
		
		// back buffer to prevent jittering
		if (buffer == null || buffer.getWidth() != getWidth()
				|| buffer.getHeight() != getHeight()) {
			buffer = new BufferedImage(getWidth(), getHeight(), 
					BufferedImage.TYPE_INT_RGB);
		}
		
		Graphics2D g = buffer.createGraphics();
		
		g.setRenderingHint(
				RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		
		// Cool lighting effect for different elevations
		int lightIndex = (int)(256 * gv.playerY() / 10000);
		if (lightIndex < -255) lightIndex = -255;
		else if (lightIndex > 255) lightIndex = 255;
		
		int color = 0xFFFFFF;
		
		int rmin = 250;
		int gmin = 255;
		if (lightIndex > 0) {
			int r = (rmin + (lightIndex * (255-rmin) / 255));
			int gr = Math.max(0, gmin-lightIndex);
			int other = 255-lightIndex;
			color = (r << 16) + (gr << 8) + (other);
		} else {
			int c = 255+lightIndex;
			int r = Math.max(0, rmin+lightIndex);
			int gr = Math.max(0, gmin+lightIndex);
			color = (r << 16) + (gr << 8) + c;
		}
		gv.wireColor = getColor(0xFFFFFF - color);
		g.setColor(getColor(color));
		
		g.fillRect(-1, -1, buffer.getWidth()+2, buffer.getHeight()+2);
		
		g.setColor(Color.BLACK);
		
		int tx = getWidth()/2;
		int ty = getHeight()/2;
		
		if (getWidth() == 0 || gv.aspectWidth == 0 
				|| getHeight() == 0 || gv.aspectHeight == 0)
			return; // nope.
		
		
		// scaling
		double scaleX = (double)gv.aspectWidth / getWidth();
		double scaleY = (double)gv.aspectHeight / getHeight();
		
		
		scaleX = 1./scaleX;
		scaleY = 1./scaleY;
		
		g.translate(tx, ty);
		g.scale(scaleX, scaleY);
		
		synchronized (gv.elock) {
		
		// center on the player
		double px = -Math.round(gv.playerX());
		double py = -Math.round(gv.playerY());
		g.translate(px, py);

		for (Art e : gv.zSortedEntities()) {
			e.draw(g);
		}
		
		for (Art d : gv.getEffects()) {
			d.draw(g);
		}
		
		g.translate(-px, -py);
		
		g.scale(1/scaleX, 1/scaleY);
		g.translate(-tx, -ty);
		
		}
		
		g.setColor(gv.wireColor);
		g.drawString("Player X: " + (int)gv.playerX(), 5, getHeight()-25);
		g.drawString("Player Y: " + (int)gv.playerY(), 5, getHeight()-5);
		
		gg.drawImage(buffer, 0, 0, getWidth(), getHeight(), null);
	}
	
	/**
	 * Returns the mouse x-position in terms of the game coordinates
	 * @return
	 */
	private int mx() {
		return 100; // TODO actually implement this for reals
	}
	
	/**
	 * Returns the mouse y-position in terms of the game coordinates
	 * @return
	 */
	private int my() {
		return 100; // TODO actually implement this for reals
	}
	
	/**
	 * Used to refresh the screen
	 * @author g
	 *
	 */
	class Repainter extends Thread {
		@Override
		public void run() {
			while (true) {
				if (gv.shouldRefreshDisplay())
					repaint();
				try {
					sleep(10);
				} catch (Exception e) {
					
				}
			}
		}
	}
	
	/**
	 * Listen for key and mouse events and pass them on to
	 * PlayerControls
	 * @author g
	 *
	 */
	class Listener implements KeyListener, MouseListener {

		@Override
		public void keyPressed(KeyEvent e) {
			if (controls != null) {
				controls.keyPressed(e.getKeyCode(), mx(), my());
			}
		}

		@Override
		public void keyReleased(KeyEvent e) {
			if (controls != null) {
				controls.keyReleased(e.getKeyCode(), mx(), my());
			}
		}

		@Override
		public void keyTyped(KeyEvent e) {
			if (controls != null) {
				controls.keyTyped(e.getKeyCode(), mx(), my());
			}
		}

		@Override
		public void mouseClicked(MouseEvent arg0) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void mouseEntered(MouseEvent arg0) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void mouseExited(MouseEvent arg0) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void mousePressed(MouseEvent arg0) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void mouseReleased(MouseEvent arg0) {
			// TODO Auto-generated method stub
			
		}
	}
}
